/*
 *  Copyright (c) 2011 Mathew Hall.
 *  All rights reserved.
 * 
 *  Redistribution and use in source and binary forms, with or
 *  without modification, are permitted provided that the following conditions
 *  are met:
 * 
 *  Redistributions of source code must retain the above copyright
 *  notice, this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above
 *  copyright notice, this list of conditions and the following
 *  disclaimer in the documentation and/or other materials provided
 *  with the distribution.
 *  Neither the name of the University of Sheffield nor the names of its
 *  contributors may be used to endorse or promote products derived
 *  from this software without specific prior written permission.
 * 
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 *  CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 *  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 *  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 *  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 *  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 *  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package search.fitnessfunctions

import primitives.cluster.*;
import primitives.graph.Node;
import primitives.graph.*;
import search.fitnessfunctions.util.EdgeSubsumptionTransformer


/**
 *
 * @author Mathew Hall
 */
class FeatureSetFF extends TreeFitnessFunction{
	
	def scache = [:]
	HashSet<Node> nodes
	
    static def buildVector(Node n){
		def ret = [:] 
		n.transitionsAsN.each{
			d ->
			ret[d] = 1
		}
		return ret;
	
	}
	
	double dist(Node node1, Node node2){
		def vec1 = buildVector(node1)
		def vec2 = buildVector(node2)
		def dist = 0;
		
		def sum1 = 0;
		def sum2 = 0;
		
		def dot = 0;
		
		nodes.each{
			n ->

			def a = 0;
			def b = 0;

			if(vec1[n] != null) a = vec1[n];
			if(vec2[n] != null) b = vec2[n];
			
			if(node1 != n && node2 != n){
				dot+= a * b
			}
			
			sum1 += a**2
			sum2 += b**2
		}
		return dot / (Math.sqrt(sum1) * Math.sqrt(sum2))
	}


   

   double getDist(Node node1, Node node2){
		if(scache == null) scache = [:]
		if(scache[node1] == null) scache[node1] = [:]
		if(scache[node1][node2] == null){
			scache[node1][node2] = dist(node1,node2)
			if(scache[node2] == null) scache[node2] = [:]
			scache[node2][node1] = scache[node1][node2]
		}
		return scache[node1][node2]
	}
    
 	double evaluate(ClusterHead tree) {
        double total = 0;

        def modules = [];

		


        tree.getChildren().each{if(it.encapsulates()) modules.add(it)}

        ClusterNode current; // = modules.first();
        def curr = 0;
        while (curr < modules.size()){
            current = modules.get(curr)
            modules.addAll(current.children.findAll{it.encapsulates()})
            curr++
        }

        def qual = [:]


		if(nodes == null)
        	nodes = tree.nodes
		
		def t_d = 0;
		def t_s = 0;

		modules.each{
			mod ->
			def sim = 0
			
			//sum similarity between nodes in mod:
			mod.nodes.each{
				node1 ->
				mod.nodes.each{
					node2 ->
					
					sim += getDist(node1,node2)
				}
			}
			t_s += sim
			def diff = 0;
			
			mod.nodes.each{
				node1 ->
				nodes.each{
					node2->
					if(!mod.nodes.contains(node2))
						diff += getDist(node1,node2)
				}
			}
			t_d += diff
			qual[mod] = [Internal: sim, External: diff]
		}
		
		return 1d / (t_d + Double.MIN_VALUE) + t_s;
		

    }

	Boolean subsumes() {
		return false;
	}
	
	
}

